package com.gao.mybatis.mapperproxy;

import com.gao.mybatis.configuration.Configuration;
import com.gao.mybatis.configuration.MappedStatement;
import com.gao.mybatis.configuration.SqlCommandType;
import com.gao.mybatis.session.SqlSession;
import lombok.Data;

import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collection;

/**
 * <p></p>
 *
 * @author GaoShuangWen
 * @since 2020-03-30 21:40
 */
public class MapperMethod {
    private final SqlCommand command;
    private final MethodSignature method;

    public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
        this.command = new SqlCommand(config, mapperInterface, method);
        this.method = new MethodSignature(config, mapperInterface, method);
    }

    public Object execute(SqlSession sqlSession, Object[] args) throws Exception {
        Object result = null;
        switch (command.getType()) {
            case INSERT: {

                break;
            }
            case UPDATE: {

                break;
            }
            case DELETE: {

                break;
            }
            case SELECT:
                if (method.returnsVoid()) {
                    result = null;
                } else if (method.returnsMany()) {
                    result = sqlSession.selectList(command.getName(), args);
                } else {
                    result = sqlSession.selectOne(command.getName(), args);

                }
                break;

            default:
                throw new RuntimeException("Unknown execution method for: " + command.getName());
        }

        return result;
    }


    @Data
    public static class SqlCommand {
        private final String name;

        private final SqlCommandType type;


        public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
            final String methodName = method.getName();
            final String declaringClassName = mapperInterface.getName();
            this.name = declaringClassName + "." + methodName;
            MappedStatement statement = configuration.getStatementMap().get(declaringClassName + "." + methodName);
            SqlCommandType sqlCommandType = statement.getSqlCommandType();
            if (sqlCommandType.equals(SqlCommandType.UNKNOWN)) {
                throw new RuntimeException("不能识别的sql命令类型");
            }
            this.type = sqlCommandType;
        }

    }

    public static class MethodSignature {

        private final boolean returnsMany;
        private final boolean returnsVoid;
        private final Class<?> returnType;


        public MethodSignature(Configuration configuration, Class<?> mapperInterface, Method method) {
            Type resolvedReturnType = TypeParameterResolver.resolveReturnType(method, mapperInterface);
            if (resolvedReturnType instanceof Class<?>) {
                this.returnType = (Class<?>) resolvedReturnType;
            } else if (resolvedReturnType instanceof ParameterizedType) {
                this.returnType = (Class<?>) ((ParameterizedType) resolvedReturnType).getRawType();
            } else {
                this.returnType = method.getReturnType();
            }
            this.returnsVoid = void.class.equals(this.returnType);
            this.returnsMany = Collection.class.isAssignableFrom(this.returnType);
        }


        public Class<?> getReturnType() {
            return returnType;
        }

        public boolean returnsMany() {
            return returnsMany;
        }


        public boolean returnsVoid() {
            return returnsVoid;
        }


    }

}
